#include "I2C2_test.h"

#define SCL(BIT) 			gpio_bit_write(I2C2_SCL_PORT, I2C2_SCL_PIN, BIT?SET:RESET)
#define SDA(BIT) 			gpio_bit_write(I2C2_SDA_PORT, I2C2_SDA_PIN, BIT?SET:RESET)
#define SDA_STATE() 	    gpio_input_bit_get(I2C2_SDA_PORT, I2C2_SDA_PIN)

#define SDA_IN()  		    gpio_mode_set(I2C2_SDA_PORT, GPIO_MODE_INPUT,  GPIO_PUPD_NONE, I2C2_SDA_PIN)
#define SDA_OUT() 		    gpio_mode_set(I2C2_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C2_SDA_PIN)

static void start();
static void stop();
static void send(uint8_t data);
static uint8_t recv();
static uint8_t wait_ack();
static void send_ack();
static void send_nack(); 


void I2C2_init() {
    // 时钟配置
    rcu_periph_clock_enable(I2C2_SCL_PORT_RCU);
    // 设置输出模式
    gpio_mode_set(I2C2_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C2_SCL_PIN);
    gpio_output_options_set(I2C2_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C2_SCL_PIN);

    // 时钟配置
    rcu_periph_clock_enable(I2C2_SDA_PORT_RCU);
    // 设置输出模式
    gpio_mode_set(I2C2_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C2_SDA_PIN);
    gpio_output_options_set(I2C2_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C2_SDA_PIN);
}

uint8_t I2C2_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    start();

    send(addr << 1);			//发送设备写地址
    if(wait_ack()) return 1;	//等待响应

    send(reg);					//发送寄存器地址
    if(wait_ack()) return 2;	//等待响应

    do {
        send(*data++);
        if(wait_ack()) return 3;
    } while(--len);

    stop();
    return 0;
}

uint8_t I2C2_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len) {
    start();

    send(addr << 1);			//发送设备写地址
    if(wait_ack()) return 1;	//等待响应

    send(reg);					//发送寄存器地址
    if(wait_ack()) return 2;	//等待响应

    do {
        send(*data);
        data += offset;
        if(wait_ack()) return 3;
    } while(--len);

    stop();
    return 0;
}

uint8_t I2C2_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    start();

    send(addr << 1);				//发送设备写地址
    if(wait_ack()) return 1;		//等待响应

    send(reg);						//发送寄存器地址
    if(wait_ack()) return 2;		//等待响应

    start();
    send((addr << 1) | 0x01);		//发送设备读地址
    if(wait_ack()) return 3;		//等待响应

    do {
        *data = recv();
        data++;
        if(len != 1) send_ack();	// 发送 NACK
    } while(--len);
    send_nack();					// 发送 NACK
    stop();

    return 0;
}

void I2C2_deinit() {
}

static void start() {
    SDA_OUT();

    SDA(1);
    delay_1us(5);
    SCL(1);
    delay_1us(5);

    SDA(0);
    delay_1us(5);
    SCL(0);
    delay_1us(5);
}

static void stop() {
    SDA_OUT();

    SCL(0);
    SDA(0);

    SCL(1);
    delay_1us(5);
    SDA(1);
    delay_1us(5);
}

static void send(uint8_t data) {
    uint8_t i;
    SDA_OUT();

    for(i = 0; i < 8; i++) {
        if(data & 0x80) {
            SDA(1);
        } else {
            SDA(0);
        }
		delay_1us(1);
        SCL(1);
        delay_1us(5);
        SCL(0);
        delay_1us(5);
        data <<= 1;
    }
}

static uint8_t recv() {
    uint8_t i, data;
    SDA_IN();
    data = 0;
    for(i = 0; i < 8; i++) {
        SCL(0);
        delay_1us(5);
        SCL(1);
        delay_1us(5);

        data <<= 1;

        data |= SDA_STATE();

        delay_1us(5);
    }
    SCL(0);
    return data;
}

static uint8_t wait_ack() {
    int8_t retry = 10;

    SCL(0);
    SDA(1);
    SDA_IN();
    delay_1us(5);
    SCL(1);
    delay_1us(5);

    while(SDA_STATE() == 1 && retry > 0) {
        retry --;
        delay_1us(5);
    }

    if(retry <= 0) {
        stop();
        return 1;
    } else {
        SCL(0);
        SDA_OUT();
    }
    return 0;
}

static void send_ack() {
    SDA_OUT();
    SCL(0);
    SDA(0);
    delay_1us(5);

    SDA(0);

    SCL(1);
    delay_1us(5);
    SCL(0);
    SDA(1);
}

static void send_nack() {
    SDA_OUT();
    SCL(0);
    SDA(0);
    delay_1us(5);

    SDA(1);

    SCL(1);
    delay_1us(5);
    SCL(0);
    SDA(1);
}